package com.orm.config;

import com.orm.base.MyMapper;
import com.orm.base.proxy.MyMapperProxy;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.type.AnnotationMetadata;
import org.springframework.core.type.classreading.CachingMetadataReaderFactory;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.util.ClassUtils;

import java.io.IOException;
import java.util.LinkedHashSet;
import java.util.Set;

public class MyBatisRegistrar implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private Environment environment;

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

        System.out.println("开始扫描=============================================");

        String basePackage = ClassUtils.getPackageName(importingClassMetadata.getClassName());

        try {
            Set<Class<?>> classes = findMyMappers(basePackage);
            for (Class<?> clazz : classes) {
                System.out.println("clazz = " + clazz);

                GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
                beanDefinition.setBeanClass(clazz);
                beanDefinition.setInstanceSupplier(() -> MyMapperProxy.createProxy(clazz));

                String beanName = clazz.getSimpleName();
                beanName = Character.toLowerCase(beanName.charAt(0)) + beanName.substring(1);
                System.out.println("注入beanName = " + beanName);
                registry.registerBeanDefinition(beanName, beanDefinition);
            }
        } catch (IOException | ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    private Set<Class<?>> findMyMappers(String basePackage) throws IOException, ClassNotFoundException {
        Set<Class<?>> candidates = new LinkedHashSet<>();
        String packageSearchPath = PathMatchingResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX + ClassUtils.convertClassNameToResourcePath(this.environment.resolveRequiredPlaceholders(basePackage)) + "/**/*.class";
        PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver();
        MetadataReaderFactory readerFactory = new CachingMetadataReaderFactory(resolver);

        for (Resource resource : resolver.getResources(packageSearchPath)) {
            if (resource.isReadable()) {
                MetadataReader reader = readerFactory.getMetadataReader(resource);
                if (isCandidateComponent(reader)) {
                    candidates.add(Class.forName(reader.getClassMetadata().getClassName()));
                }
            }
        }

        return candidates;
    }

    private boolean isCandidateComponent(MetadataReader metadataReader) throws ClassNotFoundException {
        try {
            Class<?> clazz = Class.forName(metadataReader.getClassMetadata().getClassName());
            return MyMapper.class.isAssignableFrom(clazz) && clazz.isInterface();
        } catch (Throwable ex) {
            return false;
        }
    }
}
